// @Author EthanScriptOn
// @Desc
package node

import (
	"errors"
	"reflect"
)

// DoublyLinkedNode A doubly linked list
type DoublyLinkedNode struct {
	next        *DoublyLinkedNode        // next next one
	previous    *DoublyLinkedNode        // previous previous one
	information *DoublyLinkedInformation // information Information about the current node
}

func GenerateDoublyLinkedNode(next *DoublyLinkedNode, previous *DoublyLinkedNode, prepare Node) *DoublyLinkedNode {
	information := GenerateDoublyLinkedInformation(prepare)
	return &DoublyLinkedNode{
		next:        next,
		previous:    previous,
		information: information,
	}
}

func GenerateEmptyDoublyLinkedNode(prepare Node) *DoublyLinkedNode {
	information := GenerateDoublyLinkedInformation(prepare)
	return &DoublyLinkedNode{
		information: information,
	}
}

// SlotNode Store a node that controls the direction of insertion according to the slotWay
func (d *DoublyLinkedNode) SlotNode(slotWay OptionDirection, prepare Node) error {
	direction := slotWay
	if reflect.TypeOf(prepare).Kind() != reflect.Ptr {
		panic(errors.New("prepare must ptr"))
	}
	var optionNode = d
	node, hasExtremity, err := d.GetExtremityNode(direction)
	if err != nil {
		return err
	}
	if hasExtremity {
		optionNodeTemp, ok := node.(*DoublyLinkedNode)
		if !ok {
			return errors.New("can not convert to *DoublyLinkedList")
		}
		optionNode = optionNodeTemp
	}
	switch direction {
	case HEAD:
		prepareNode := GenerateDoublyLinkedNode(nil, optionNode, prepare)
		d.next = prepareNode
	case TAIL:
		prepareNode := GenerateDoublyLinkedNode(optionNode, nil, prepare)
		d.previous = prepareNode
	}
	return nil
}

// GetExtremityNode Get the last node under Directions
func (d *DoublyLinkedNode) GetExtremityNode(direction OptionDirection) (Node, bool, error) {
	currentNode := d
	for {
		foundNode, canCirculate, err := currentNode.GetAdjacentNode(direction)
		if err != nil {
			return nil, false, err
		}
		if !canCirculate {
			return currentNode, true, nil
		}
		childNode, ok := foundNode.(*DoublyLinkedNode)
		if !ok {
			return nil, false, errors.New("foundNode can not convert to *DoublyLinkedList")
		}
		currentNode = childNode
	}
}

// GetAdjacentNode Get the next node under the direction
func (d *DoublyLinkedNode) GetAdjacentNode(direction OptionDirection) (Node, bool, error) {
	var node Node = nil
	switch direction {
	case HEAD:
		node = d.previous
	case TAIL:
		node = d.next
	}
	if node == nil {
		return nil, false, nil
	}
	adjacentNode, ok := node.(*DoublyLinkedNode)
	if !ok {
		return nil, false, errors.New("adjacentNode can not convert to *DoublyLinkedList")
	}
	return adjacentNode, true, nil
}

// DetachNode Removes the current node
func (d *DoublyLinkedNode) DetachNode() error {
	previousNode := d.previous
	nextNode := d.next
	if previousNode != nil {
		previousNode.next = nextNode
	}
	if nextNode != nil {
		nextNode.previous = previousNode
	}
	return nil
}

// GetPrepare Get the previous node
func (d *DoublyLinkedNode) GetPrepare() Node {
	return d.information.elem
}
